package com.blog.admin.service.impl;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson2.JSON;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PutObjectResult;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blog.admin.config.OssConfiguration;
import com.blog.admin.listener.event.CacheEvent;
import com.blog.admin.service.FileInfoService;
import com.blog.admin.service.PageService;
import com.blog.common.core.enums.FileEnums;
import com.blog.common.core.enums.PageCoverEnum;
import com.blog.common.core.enums.PathEnum;
import com.blog.common.core.text.Convert;
import com.blog.common.core.utils.StringUtils;
import com.blog.common.domain.adapter.ConvertAdapter;
import com.blog.common.domain.entity.*;
import com.blog.admin.service.SiteConfigService;
import com.blog.common.mapper.dao.FileInfoMapper;
import com.blog.common.mapper.dao.PhotoAlbumMapper;
import com.blog.common.mapper.dao.PhotoMapper;
import com.blog.common.mapper.dao.SiteConfigMapper;
import com.blog.common.redis.service.RedisService;
import com.blog.system.api.feign.RemotePictureService;
import feign.Response;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.blog.common.core.constant.CacheConstants.FILE_INFO;
import static com.blog.common.core.constant.CacheConstants.WEBSITE_CONFIG;
import static com.blog.common.core.constant.CommonConstants.ORDER;
import static com.blog.common.core.constant.CommonConstants.RANDOM;

/**
 * <p>
 * 网站配置 服务实现类
 * </p>
 *
 * @author Valerie
 * @since 2023-02-18
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SiteConfigServiceImpl extends ServiceImpl<SiteConfigMapper, SiteConfig>
        implements SiteConfigService {


    private final RedisService redisService;

    private final FileInfoService fileInfoService;

    private final FileInfoMapper fileInfoMapper;

    private final PhotoMapper photoMapper;

    private final OSS oss;

    private final PhotoAlbumMapper photoAlbumMapper;

    private final OssConfiguration ossConfiguration;

    private final SiteConfigMapper siteConfigMapper;

    private final RemotePictureService remotePictureService;

    private final PageService pageService;

    private final ApplicationEventPublisher eventPublisher;


    @Override
    public SiteConfig getSiteConfig() {

        SiteConfig siteConfig = (SiteConfig) redisService.get(WEBSITE_CONFIG);
        if (Objects.nonNull(siteConfig)) {
            return siteConfig;
        }
        siteConfig = this.getOne(new LambdaQueryWrapper<SiteConfig>().last("limit 1"));
        redisService.set(WEBSITE_CONFIG, siteConfig);
        redisService.expire(WEBSITE_CONFIG, 5L, TimeUnit.HOURS);
        return siteConfig;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    public void savePageCover() {
        final String suffix = ".jpeg";
        final String fileSuffix = "/";
        Response response;

        // 单数
        if (((LocalDateTime.now().getDayOfMonth()) & 1) == 0) {
            response = remotePictureService.getPageCoverByLandscape();
        } else {
            response = remotePictureService.getPageCoverByAnime();
        }
        Response.Body body = response.body();
        if (Objects.isNull(body)) {
            return;
        }
        String path = PathEnum.FILE.getPath() + IdUtil.getSnowflakeNextIdStr() + suffix;
        PutObjectResult result;
        byte[] bytes = IoUtil.readBytes(body.asInputStream());
        String md5;

        // 上传oss
        try (InputStream md5Stream = IoUtil.toStream(bytes);
             InputStream fileStream = IoUtil.toStream(bytes);
        ) {
            md5 = DigestUtil.md5Hex(md5Stream);
            Long count = fileInfoMapper.selectCount(new LambdaQueryWrapper<FileInfo>().eq(FileInfo::getMd5, md5));
            if (count > 0) {
                return;
            }
            result = oss.putObject(ossConfiguration.getBucketName(), path, fileStream);
        } catch (OSSException | ClientException e) {
            throw new RuntimeException(e);
        }

        Long fileId = IdUtil.getSnowflakeNextId();
        if (Objects.nonNull(result)) {
            FileInfo fileInfo = FileInfo.builder()
                    .id(fileId)
                    .filePath(fileSuffix + path)
                    .cdnPath(ossConfiguration.getCdnPath())
                    .originPath(ossConfiguration.getUrl())
                    .md5(md5)
                    .fileType(FileEnums.SYSTEM_FILE.getCode())
                    .fileName("壁纸")
                    .build();
            PhotoAlbum photoAlbum = photoAlbumMapper.selectOne(new LambdaQueryWrapper<PhotoAlbum>()
                    .eq(PhotoAlbum::getAlbumName, "壁纸")
                    .last("limit 1"));
            Photo photo = Photo.builder()
                    .albumId(photoAlbum.getId())
                    .photoName("壁纸")
                    .photoSrc(fileId.toString())
                    .build();
            photoMapper.insert(photo);
            fileInfoMapper.insert(fileInfo);
            log.info("获取二次元图片：name->{}", path);
            eventPublisher.publishEvent(new CacheEvent(this, FILE_INFO));
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @Lock4j(name = "pageCover", acquireTimeout = 1000)
    public void setSitePageCover(Serializable methodType) {

        ConvertAdapter.skipConvert();
        SiteConfig siteConfig = siteConfigMapper.selectOne(new LambdaQueryWrapper<SiteConfig>().last("limit 1"));
        // 最新壁纸
        List<FileInfo> fileInfoList = new LambdaQueryChainWrapper<>(fileInfoMapper)
                .eq(FileInfo::getStatus, PageCoverEnum.AVAILABLE.getCode())
                .orderByDesc(FileInfo::getCreateTime)
                .select(FileInfo::getId)
                .list();
        switch (Integer.parseInt(methodType.toString())) {
            case RANDOM:
                Collections.shuffle(fileInfoList);
                fileInfoList = fileInfoList.stream().limit(6).collect(Collectors.toList());
                break;
            case ORDER:
            default:
                fileInfoList = fileInfoList.stream().limit(6).collect(Collectors.toList());
        }
        List<String> fileInfoIdList = fileInfoList.stream().map(e -> e.getId().toString()).collect(Collectors.toList());

        boolean updated = false;
        // 修改之前的壁纸
        if (CollectionUtil.isNotEmpty(siteConfig.getPageCover())) {
            updated = new LambdaUpdateChainWrapper<>(fileInfoMapper)
                    .in(FileInfo::getId, siteConfig.getPageCover())
                    .set(FileInfo::getStatus, PageCoverEnum.AVAILABLE.getCode())
                    .set(FileInfo::getRemark, "主页壁纸移除")
                    .update();
        }
        if (!updated){
            log.warn("修改首页壁纸失败");
        }
        siteConfig.setPageCover(fileInfoIdList);
        // 设置新壁纸
        siteConfigMapper.updateById(siteConfig);
        // 修改状态
        if (CollectionUtil.isNotEmpty(fileInfoList)) {
            new LambdaUpdateChainWrapper<>(fileInfoMapper)
                    .in(FileInfo::getId, fileInfoIdList)
                    .set(FileInfo::getStatus, PageCoverEnum.OCCUPIED.getCode())
                    .set(FileInfo::getRemark, "主页壁纸设置")
                    .update();
        }

        // 设置其他的页面
        List<FileInfo> anotherFileInfoList = new LambdaQueryChainWrapper<>(fileInfoMapper)
                .eq(FileInfo::getStatus, PageCoverEnum.AVAILABLE.getCode())
                .orderByDesc(FileInfo::getCreateTime)
                .list();
        List<Page> pageList = pageService.list();
        List<Long> pageCoverIdList = pageList.stream().map(Page::getPageCover)
                .filter(Objects::nonNull)
                .filter(StringUtils::isNotBlank)
                .map(Long::parseLong)
                .collect(Collectors.toList());

        switch (Integer.parseInt(methodType.toString())) {
            case RANDOM:
                Collections.shuffle(anotherFileInfoList);
                anotherFileInfoList = anotherFileInfoList.stream()
                        .limit(pageList.size())
                        .collect(Collectors.toList());
                break;
            case ORDER:
            default:
                anotherFileInfoList = anotherFileInfoList.stream()
                        .sorted(Comparator.comparing(FileInfo::getCreateTime).reversed())
                        .limit(pageList.size())
                        .collect(Collectors.toList());
                break;
        }
        boolean update = false;
        // 移除之前的壁纸
        if (CollectionUtil.isNotEmpty(pageCoverIdList)) {
            update = new LambdaUpdateChainWrapper<>(fileInfoMapper)
                    .in(FileInfo::getId, pageCoverIdList)
                    .set(FileInfo::getStatus, PageCoverEnum.AVAILABLE.getCode())
                    .set(FileInfo::getRemark, "其他页壁纸移除")
                    .update();
        }
        if (!update) {
            log.warn("修改其他页面壁纸失败！");
        }
        // 设置其他页面壁纸
        for (int i = 0, j = 0; i < pageList.size() && j < anotherFileInfoList.size(); i++, j++) {
            FileInfo fileInfo = anotherFileInfoList.get(j);
            pageList.get(i).setPageCover(fileInfo.getId().toString());
            fileInfo.setStatus(PageCoverEnum.OCCUPIED.getCode());
            fileInfo.setRemark("其他页壁纸设置");
        }
        pageService.updateBatchById(pageList);
        fileInfoService.updateBatchById(anotherFileInfoList);
        log.info("设置页面图片！");
        redisService.del(WEBSITE_CONFIG);
        eventPublisher.publishEvent(new CacheEvent(this, FILE_INFO));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    @Lock4j(name = "pageCover", acquireTimeout = 1000)
    public void setSiteAnotherPageCover(Serializable methodType) {
        ConvertAdapter.skipConvert();

    }
}




